#include "MlpNetwork.h"

namespace fullsail_ai {

	void MlpNetwork::initialize(int (*numberGenerator)(), std::size_t inputCount,
	                            std::size_t hiddenNeuronCount, std::size_t actionCount)
	{
		// TODO: 2
		// Initialize the hidden layer, then the output layer.
		hiddenLayer.initialize(inputCount,hiddenNeuronCount,numberGenerator);
		outputLayer.initialize(hiddenNeuronCount,actionCount,numberGenerator);
	}

	void MlpNetwork::generateOutputs(std::vector<double> const& inputs,
	                                 std::vector<double>& outputs) const
	{
		// TODO: 8
		// Generate the outputs of the network by running the inputs through
		// the appropriate layers.
		std::vector<double> temp;
		temp.resize(hiddenLayer.getNeurons().size());
		hiddenLayer.feedForward(inputs,temp);
		outputLayer.feedForward(temp,outputs);
	}

	void MlpNetwork::computeOutputErrors(std::vector<double> const& expectedOutputs,
	                                     std::vector<double> const& actualOutputs,
	                                     std::vector<double>& outputErrors)
	{
		// TODO: 10
		// Compute the error between each expected output and the corresponding actual output.
		for(unsigned int i = 0; i < actualOutputs.size();++i)
		{
			double error = (expectedOutputs[i] - actualOutputs[i])*(actualOutputs[i]*(1-actualOutputs[i]));
			outputErrors[i] = error;
		}
	}

	void MlpNetwork::learnTrainingSets(std::vector<std::vector<double> > const& trainingSetInputs,
	                                   std::vector<std::vector<double> > const& expectedOutputs,
	                                   double learningFactor)
	{
		// TODO: 13
		// Train the network using the provided training set inputs and expected outputs.
		for(unsigned int i = 0; i < trainingSetInputs.size();++i)
		{
			std::vector<double> actVals;
			actVals.resize(hiddenLayer.getNeurons().size());
			hiddenLayer.feedForward(trainingSetInputs[i],actVals);

			std::vector<double> outPuts;
			outPuts.resize(outputLayer.getNeurons().size());
			outputLayer.feedForward(actVals,outPuts);

			std::vector<double> outPutErrors;
			outPutErrors.resize(expectedOutputs[i].size());
			computeOutputErrors(expectedOutputs[i],outPuts,outPutErrors);

			std::vector<double> hiddenErrors;
			hiddenErrors.resize(actVals.size());
			computeHiddenErrors(actVals,outPutErrors,hiddenErrors);

			outputLayer.backPropagate(actVals,outPutErrors,learningFactor);
			hiddenLayer.backPropagate(trainingSetInputs[i],hiddenErrors,learningFactor);

		}
	}
}  // namespace fullsail_ai

